package com.jgp.security.shiro;

import com.jgp.security.annotation.Power;
import com.jgp.sys.annotation.PowerGroup;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.aop.AuthorizingAnnotationHandler;
import org.apache.shiro.subject.Subject;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 * 项目   parent
 * 作者   loufei
 * 时间   2018/4/13
 */
public class AdminPermissionAnnotationHandler extends AuthorizingAnnotationHandler {

    /**
     * Default no-argument constructor that ensures this handler looks for
     * {@link org.apache.shiro.authz.annotation.RequiresPermissions RequiresPermissions} annotations.
     */
    public AdminPermissionAnnotationHandler() {
        super(Power.class);
    }

    /**
     * Returns the annotation {@link Power#value value}, from which the Permission will be
     * constructed.
     *
     * @param a the RequiresPermissions annotation being inspected.
     * @return the annotation's <code>value</code>, from which the Permission will be constructed.
     */
    protected String[] getAnnotationValue(Annotation a) {
        Power rpAnnotation = (Power) a;
        return rpAnnotation.value();
    }

    /**
     * Ensures that the calling <code>Subject</code> has the Annotation's specified permissions, and if
     * not, throws an
     * <code>AuthorizingException</code> indicating access is denied.
     *
     * @param a the RequiresPermission annotation being inspected to check for one or more permissions
     * @throws org.apache.shiro.authz.AuthorizationException if the calling <code>Subject</code> does not
     * have the permission(s) necessary to
     *                                                       continue access or execution.
     */
    public void assertAuthorized(Annotation a, Method method) throws AuthorizationException {
        if (!(a instanceof Power)) return;
        Power rpAnnotation = (Power) a;
        String[] perms = getAnnotationValue(a);
        Subject subject = getSubject();

        if (perms.length == 0) {
            Class<?> clazz = method.getDeclaringClass();
            PowerGroup groupPower = clazz.getAnnotation(PowerGroup.class);
            String groupClassName = "DEFAULT";
            if(Objects.nonNull(groupPower)) {
                groupClassName = clazz.getName();
            }

            String methodName = method.getName();
            subject.checkPermission(groupClassName+":"+methodName);
            return;
        }

        if (perms.length == 1) {
            subject.checkPermission(perms[0]);
            return;
        }
        if (Logical.AND.equals(rpAnnotation.logical())) {
            getSubject().checkPermissions(perms);
            return;
        }
        if (Logical.OR.equals(rpAnnotation.logical())) {
            // Avoid processing exceptions unnecessarily - "delay" throwing the handler by calling
            // hasRole first
            boolean hasAtLeastOnePermission = false;
            for (String permission : perms)
                if (getSubject().isPermitted(permission)) hasAtLeastOnePermission = true;
            // Cause the handler if none of the role match, note that the handler message will be a bit
            // misleading
            if (!hasAtLeastOnePermission) getSubject().checkPermission(perms[0]);

        }
    }

    @Override
    public void assertAuthorized(Annotation a) throws AuthorizationException {
        assertAuthorized(a, null);
    }
}